package com.leyou.sh.auth.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leyou.sh.auth.dto.Payload;
import com.leyou.sh.auth.dto.UserDetail;
import com.leyou.sh.common.exception.LyException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.leyou.sh.auth.dto.JwtConstants.KEY_PREFIX;

/**
 * @author balisong_fcs
 * @date 2021/1/11 20:30
 * @desc
 */
public class JwtUtils {



    /**
     * JWT解析器
     */
    private final JwtParser jwtParser;
    /**
     * 秘钥
     */
    private final SecretKey secretKey;

    private final static ObjectMapper mapper = new ObjectMapper();

    /**
     * 设置有效时长有效token,引入stringRedisTemplate
     */

    private StringRedisTemplate redisTemplate;

    public JwtUtils(String key,StringRedisTemplate redisTemplate) {
        // 生成秘钥
        secretKey = Keys.hmacShaKeyFor(key.getBytes(Charset.forName("UTF-8")));
        // JWT解析器
        this.jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
        this.redisTemplate = redisTemplate;
    }

    /**
     * 生成jwt，用默认的JTI
     *
     * @param userDetails 用户信息
     * @return JWT
     */
  /*  public String createJwt(Object userDetails) {
        return createJwt(userDetails, 1800);
    }*/

    /**
     * 生成jwt，自己指定的JTI
     *
     * @param userDetails 用户信息
     * @return JWT 即JWt就是token,token就是jwt
     */
    public String createJwt(UserDetail userDetails) {
        String jti = createJti();
        try {
            // 生成token
            String token = Jwts.builder().signWith(secretKey)
                    //保持和上面jti一致
                    .setId(jti)
                    .claim("user", mapper.writeValueAsString(userDetails))
                    .compact();
            //登录时,生成的token ,吧当前token的jti存入redis中;设置key唯一,并且不重复
            this.redisTemplate.opsForValue().set(KEY_PREFIX + userDetails.getId(), jti, 30, TimeUnit.MINUTES);
            return token;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析jwt，并将用户信息转为指定的Clazz类型
     *
     * @param jwt   token
     * @return 载荷，包含JTI和用户信息
     */
    public Payload parseJwt(String jwt) {
        try {
            Jws<Claims> claimsJws = jwtParser.parseClaimsJws(jwt);
            Claims claims = claimsJws.getBody();

            Payload payload = new Payload();
            payload.setJti(claims.getId());
            payload.setUserDetail(mapper.readValue(claims.get("user", String.class), UserDetail.class));

            String key = KEY_PREFIX + payload.getUserDetail().getId();

            if (this.redisTemplate.hasKey(key) && claims.getId().equals(redisTemplate.opsForValue().get(key))) {
                return payload;
            }
            throw new LyException(401, "token失效,或用户未登录");

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String createJti() {
        return StringUtils.replace(UUID.randomUUID().toString(), "-", "");
    }
}
